package com.mutual.assistance.exception.handle;

import com.mutual.assistance.exception.BizException;
import com.mutual.assistance.exception.code.ExceptionCode;
import com.mutual.assistance.response.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;

import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;


@ControllerAdvice(annotations = {RestController.class, Controller.class})
@ResponseBody
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(BizException.class)
    @ResponseStatus(HttpStatus.OK)
    public R<String> bizException(BizException ex) {
        log.warn("BizException: {}", ex.getMessage(), ex);
        ex.printStackTrace();
        return R.fail(ex.getCode(), ex.getMessage());
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.OK)
    public R<Map<String, Object>> MethodArgumentNotValid(MethodArgumentNotValidException ex) {
        log.warn("MethodArgumentNotValidException: {}", ex.getMessage(), ex);
        List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();
        Map<String, Object> errorMap = fieldErrors.stream()
                .collect(Collectors.toMap(FieldError::getField, DefaultMessageSourceResolvable::getDefaultMessage));
        return R.result(ExceptionCode.BASE_VALID_PARAM.getCode(), errorMap, ExceptionCode.BASE_VALID_PARAM.getMsg());
    }

    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.OK)
    public R<Map<String, Object>> MethodArgumentNotValid(BindException ex) {
        log.warn("BindException: {}", ex.getMessage(), ex);
        ex.printStackTrace();
        List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();
        Map<String, Object> errorMap = fieldErrors.stream()
                .collect(Collectors.toMap(FieldError::getField, DefaultMessageSourceResolvable::getDefaultMessage));
        return R.result(ExceptionCode.BASE_VALID_PARAM.getCode(), errorMap, ExceptionCode.BASE_VALID_PARAM.getMsg());
    }

    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    @ResponseStatus(HttpStatus.OK)
    public R<String> methodArgumentTypeMismatchException(MethodArgumentTypeMismatchException ex) {
        log.warn("MethodArgumentTypeMismatchException:", ex);
        ex.printStackTrace();
        String msg = "参数：[" + ex.getName() +
                "]的传入值：[" + ex.getValue() +
                "]与预期的字段类型：[" + Objects.requireNonNull(ex.getRequiredType()).getName() + "]不匹配";
        return R.fail(ExceptionCode.PARAM_EX.getCode(), msg);
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    @ResponseStatus(HttpStatus.OK)
    public R<String> missingServletRequestParameterException(MissingServletRequestParameterException ex) {
        log.warn("MissingServletRequestParameterException:{}", ex.getMessage(), ex);
        ex.printStackTrace();
        String msg = "缺少必须的[" + ex.getParameterType() + "]类型的参数[" + ex.getParameterName() + "]";
        return R.fail(ExceptionCode.ILLE_GALA_ARGUMENT_EX.getCode(), msg);
    }

    @ExceptionHandler(AccessDeniedException.class)
    @ResponseStatus(HttpStatus.OK)
    public R<String> doAccessDeniedException(Exception ex) {
        log.warn("AccessDeniedException: {}", ex.getMessage(), ex);
        ex.printStackTrace();
        return R.fail(ExceptionCode.UNAUTHORIZED.getCode(), ExceptionCode.UNAUTHORIZED.getMsg());
    }

    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.OK)
    public R<String> otherExceptionHandler(Exception ex, HttpServletRequest request) {
        log.warn("Exception: {}", ex.getMessage(), ex);
        ex.printStackTrace();
        return R.fail(ExceptionCode.INTERNAL_SERVER_ERROR.getCode(), ExceptionCode.INTERNAL_SERVER_ERROR.getMsg());
    }
}
